// Copyright (C) Calum Grant 2008

namespace dynamic
{
	class var;
	class var_impl;

	typedef std::basic_string<char> string;
	typedef std::basic_string<wchar_t> wstring;


	/// The internal type-id of a variable.
	/** This enum is used for two things: 
		1) For comparing different vars, and
		2) For pickling and unpickling.
		Do not change the order of these enums or add numbers to the middle of
		the list, because otherwise pickled data would not be readable.
		*/
	enum var_cmp_index 
	{ 
		cmp_null, 
		cmp_false, 
		cmp_true, 
		cmp_char, 
		cmp_wchar, 
		cmp_int, 
		cmp_double, 
		cmp_string, 
		cmp_wstring, 
		cmp_array, 
		cmp_list, 
		cmp_set, 
		cmp_multiset, 
		cmp_map, 
		cmp_multimap, 
		cmp_reference, 
		cmp_object, 
		cmp_function_0, cmp_function_v0, 
		cmp_function_1, cmp_function_v1, 
		cmp_function_2, cmp_function_v2, 
		cmp_function_3, cmp_function_v3, 
		cmp_function_4, cmp_function_v4, 
		cmp_function_5, cmp_function_v5, 
		cmp_function_6, cmp_function_v6, 
		cmp_function_7, cmp_function_v7, 
		cmp_function_8, cmp_function_v8, 
		cmp_function_9, cmp_function_v9, 
		cmp_function_10, cmp_function_v10, 
		cmp_bind,
		cmp_native, 
		cmp_sequence, 
		cmp_varargs,
		cmp_proxy, 
		cmp_reverse,
		cmp_dispatcher, 
		cmp_filter, 
		cmp_transform, 
		cmp_tail,
		cmp_python,
		cmp_user = 1000
	};


	/// The result of comparing two vars.
	enum var_cmp_result { cmp_equal, cmp_not_equal, cmp_lt, cmp_gt };


	namespace internal
	{
		class extender;
		class var_deref;

		/// Defines methods for var, and a number of helper classes which behave like var.
		/** @param Derived is the derived class that we static_cast to
			@param Deref is the type used to access the underlying var
			\ingroup syntax*/
		template<typename Derived, typename Deref>
		class var_methods
		{
		public:
			/// Add.
			template<typename T> var operator+(const T & t) const;

			/// Subtract.
			template<typename T> var operator-(const T & t) const;

			/// Multiply.
			template<typename T> var operator*(const T & t) const;

			/// Divide.
			template<typename T> var operator/(const T & t) const;

			/// Modulo.
			template<typename T> var operator%(const T & t) const;

			/// Right-shift.
			template<typename T> var operator>>(const T & t) const;

			/// Left-shift.
			template<typename T> var operator<<(const T & t) const;

			/// Negation.
			var operator-() const;

			/// Unary add, usually clones.
			var operator+() const;

			/// Bitwise inverse.
			var operator~() const;

			/// Access iterator value.
			var_deref operator->() const;

			/// Dereference iterator.
			var operator*() const;

			/// Returns iterator to container contents.
			var begin() const;

			/// Returns iterator to end of container contents.
			var end() const;

			/// Returns the number of items in the container.
			int size() const;

			/// Returns whether the container is empty.
			bool empty() const;

			/// Returns a reverse iterator to the container.
			var rbegin() const;

			/// Returns the end reverse iterator to the container.
			var rend() const;

			/// Returns a string representation of the object.
			string as_string() const;

			/// Returns a wide string representation of the object.
			wstring as_wstring() const;

			/// Returns an integer representation of the object.
			int as_int() const 
			{ 
				return deref().impl().as_int(); 
			}

			/// Returns a double representation of the object.
			double as_double() const 
			{ 
				return deref().impl().as_double(); 
			}

			/// Convert the object to a bool.
			bool as_bool() const 
			{ 
				return deref().impl().as_bool(); 
			}

			/// Same as as_bool().
			operator bool() const 
			{ 
				return as_bool(); 
			}

			/// Logical not operator.
			bool operator!() const 
			{ 
				return !as_bool(); 
			}

			/// Convert the var to an int, or returns 0.
			/** Always succeeds */
			operator int() const 
			{ 
				return as_int(); 
			}

			/// Creates a clone.
			/** Always succeeds, unless we get a memory exception (std::bad_alloc) */
			var clone() const;

			/// Resizes the contents for certain datatypes.
			/** Throws "unsupported" if the underlying object does not support the operation */
			void resize(const var & size);

			/// Erases all items in the object.
			/** Always succeeds, and has no effect on non-containers. */
			void clear();

			/// Adds an item to the back of the container.
			void push_back(const var&);

			/// Removes an item from the back of the container, and returns it.
			var pop_back();

			/// Pushes an item to the front of the container.
			void push_front(const var&);

			/// Removes an item from the front of the container, and returns it.
			var pop_front();

			/// Inserts an item into the container.
			void insert(const var & item);

			/// Inserts a pair of items into the container (for maps).
			void insert(const var & position, const var & item);

			/// Returns the first item in the container.
			var front() const;

			/// Returns the last item in the container.
			var back() const;

			/// Returns the key of the iterator.
			var key() const;

			/// Returns the value of the iterator.
			var value() const;

			/// Tests whether the container contains the given item.
			/** This always succeeds but by default returns false. */
			bool contains(const var & item) const;

			/// Erases the given item from the container.
			/** This always succeeds but has not effect if not implemented or
				the container does not contain the item. */
			void erase(const var & item);

			/// Merges the members of one object into another.
			void mixin(const var & object);

			/// Compare for equality using compare().
			template<typename T>
			bool operator==(const T & v) const 
			{ return deref().impl().compare(v) == cmp_equal; }

			/// Compare for not equals.
			template<typename T>
			bool operator!=(const T & v) const 
			{ return deref().impl().compare(v) != cmp_equal; }

			/// Compare for less-than.
			template<typename T>
			bool operator<(const T & v) const 
			{ return deref().impl().compare(v) == cmp_lt; }

			/// Compare for greater-than.
			template<typename T>
			bool operator>(const T & v) const 
			{ return deref().impl().compare(v) == cmp_gt; }

			/// Compare for less-than-or-equals.
			template<typename T>
			bool operator<=(const T & v) const 
			{ return deref().impl().compare(v) != cmp_gt; }

			/// Compare for greater-than-or-equals.
			template<typename T>
			bool operator>=(const T & v) const 
			{ return deref().impl().compare(v) != cmp_lt; }

			/// Extract the stored type from create().
			template<typename T> T & as();
			
			/// Extract the stored type (without type check).
			template<typename T> T & as_unsafe() 
			{ return *static_cast<T*>(deref().impl().as_ptr()); }

			/// Preincrement operator.
			Derived & operator++()
			{
				deref().impl().op_inc();
				return static_cast<Derived&>(*this);
			}

			/// Postincrement operator.
			var operator++(int);

			/// Predecrement operator.
			Derived & operator--()
			{
				deref().impl().op_dec();
				return static_cast<Derived&>(*this);
			}

			/// Postdecrement operator.
			var operator--(int);

			/// Function call.
			var operator()() const;

			/// Function call.
			var operator()(const var &) const;

			/// Function call.
			var operator()(const var &, const var&) const;

			/// Function call.
			var operator()(const var &, const var&, const var&) const;

			/// Function call.
			var operator()(const var &, const var&, const var&, const var&) const;

			/// Function call.
			var operator()(const var &, const var&, const var&, const var&, const var&) const;

			/// Function call.
			var operator()(const var &, const var&, const var&, const var&, const var&, const var&) const;

			/// Function call.
			var operator()(const var &, const var&, const var&, const var&, const var&, const var&, const var&) const;

			/// Function call.
			var operator()(const var &, const var&, const var&, const var&, const var&, const var&, const var&, const var&) const;

			/// Function call.
			var operator()(const var &, const var&, const var&, const var&, const var&, const var&, const var&, const var&, const var&) const;

			/// Function call.
			var operator()(const var &, const var&, const var&, const var&, const var&, const var&, const var&, const var&, const var&, const var&) const;

			/// Returns the class name of the object.
			std::string class_name() const;
			
			/// Returns a container of the keys (default is null).
			var keys() const;

			/// Returns a container of values (default is null).
			var values() const;

			/// Returns the maximum number of arguments that the functor can be called with.
			int max_args() const;

			/// Returns a threadsafe proxy to this object.
			var proxy() const;

			/// Returns an enumerator to the contents of the object (default null).
			var enumerator() const;

			/// Returns an enumerator to traverse the contents in reverse order (default null).
			var reverse_enumerator() const;

			/// Extend the object with a sequence of name-value pairs.
			template<typename T> dynamic::internal::extender extend(const char *m, T t);

		private:
			/// Access the underlying var.
			Deref deref() const 
			{ 
				return static_cast<const Derived*>(this)->deref(); 
			}
		};
	}
}
